/*-
 * Copyright (c) 2007-2008 SINA Corporation, All Rights Reserved.
 * Author: Zhang shuo <zhangshuo@staff.sina.com.cn>
 * A ligth weight and fast buffer implementation.
 */
 
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>

#include "buffer.h"


static char *bsd_strnstr(const char *s, const char *find, size_t slen);


/* ======== conditon buffer part ========= */


/* initialize the buffer. you must have init done when you ready to use
 * this condition buffer */
CONDITION_BUF * 
condition_buf_init(const char *condition_str)
{
	struct con_buf *condition_buffer;
	
	condition_buffer = (struct con_buf *)BUF_MALLOC(sizeof(struct con_buf));
	if (condition_buffer == NULL)
    {
        return NULL;
    }
		
	condition_buffer->conditon_str_len = strlen(condition_str);
	if (condition_buffer->conditon_str_len >= CONDITION_STR_MAX_LEN)return NULL;
	
	condition_buffer->buf_length = CDT_BUF_SIZE;
	condition_buffer->buf_content_len = 0;
	condition_buffer->remain_data = NULL;
	condition_buffer->remain_data_len = 0;
	condition_buffer->bytes_read = 0;
	
	//memset(condition_buffer->buf_ptr, 0, CDT_BUF_SIZE + 1);
	memset(condition_buffer->condition_str, 0, CONDITION_STR_MAX_LEN);
	memmove(condition_buffer->condition_str, condition_str, condition_buffer->conditon_str_len);
	
	return condition_buffer;
}


/* the entry of buffer */
int
condition_buf(CONDITION_BUF *buf_handler, char *data, size_t len)
{
	size_t free_bytes;
	char *search_point;
	size_t search_len;
	char *find_ptr;
	
	
	/* check whether the data is available */
	if (buf_handler == NULL || data == NULL || len <= 0)
	{
		return NO_DATA;
	}
	else
	{
		free_bytes = buf_handler->buf_length - buf_handler->bytes_read;

		if ((ssize_t)(buf_handler->bytes_read - buf_handler->conditon_str_len) < 0)
		{
			/* first time, search from buffer head */
			search_point = buf_handler->buf_ptr;
		}
		else
		{
			/* otherwise, search from the (last break point - condition string length). */
			search_point = buf_handler->buf_ptr + (buf_handler->bytes_read - buf_handler->conditon_str_len);
		}
		
		/* if the free buffer is smaller than 'len',
		 * write data to buffer as more as possible, if return FOUND_AND_FULL.
		 * it means that condition_str is found but some entity data will be
		 * dropped if it exist. */
		 
		if (free_bytes < len)
		{
			memmove(buf_handler->buf_ptr + buf_handler->bytes_read, data, free_bytes);
			buf_handler->bytes_read += free_bytes;
			search_len = buf_handler->bytes_read + buf_handler->conditon_str_len;
			
			if (search_len > buf_handler->bytes_read)
			{
				search_len = buf_handler->bytes_read;
			}
			
			/* find whether the condition string appeared */
			find_ptr = bsd_strnstr(search_point, buf_handler->condition_str, search_len);
			
			if (find_ptr != NULL)
			{
				buf_handler->buf_content_len = (size_t)(find_ptr - buf_handler->buf_ptr);
				buf_handler->remain_data = NULL;
				buf_handler->remain_data_len = 0;
				return FOUND_AND_FULL;
			}
			
			return BUF_IS_FULL;
		}
		else
		{
			memmove(buf_handler->buf_ptr + buf_handler->bytes_read, data, len);
			buf_handler->bytes_read += len;
			search_len = buf_handler->bytes_read + buf_handler->conditon_str_len;
			
			if (search_len > buf_handler->bytes_read)
			{
				search_len = buf_handler->bytes_read;
			}
			
			find_ptr = bsd_strnstr(search_point, buf_handler->condition_str, search_len);
		
			if (find_ptr != NULL)
			{
				buf_handler->buf_content_len = (size_t)(find_ptr - buf_handler->buf_ptr);
				buf_handler->remain_data = find_ptr + buf_handler->conditon_str_len;
				buf_handler->remain_data_len = (size_t)(buf_handler->bytes_read - (find_ptr - buf_handler->buf_ptr + buf_handler->conditon_str_len));
				return FOUND;
			}
		}
	}
	
	return CONTINUE_BUF;
}


/* if you do not what to free the buffer and want to use again. an reset
 * is required. this feature often be used in a keep-alive TCP connection */
void
condition_buf_reset(CONDITION_BUF *buf_handler)
{
	buf_handler->buf_length = CDT_BUF_SIZE;
	buf_handler->buf_content_len = 0;
	buf_handler->remain_data = NULL;
	buf_handler->remain_data_len = 0;
	buf_handler->bytes_read = 0;
	//memset(buf_handler->buf_ptr, 0, CDT_BUF_SIZE + 1);
}


/* if your want to destory the buffer, clean is the best */
int 
condition_buf_clean(CONDITION_BUF *buf_handler)
{
	if (buf_handler == NULL)
	{
        return -1;
	}
	
	BUF_FREE(buf_handler);
	
	return 0;
}
/* ======== end of conditon buffer part ========= */




/* ======== fix length buffer part ========= */

/* initialize the buffer. you must have init done when you ready to use
 * this fixed length buffer */
LENGTH_BUF * 
length_buf_init()
{
	struct len_buf *length_buffer;
	
	length_buffer = (struct len_buf *)BUF_MALLOC(sizeof(struct len_buf));

    if (length_buffer == NULL)
    {
        return NULL;
    }

	
	length_buffer->buf_length = 0;
	length_buffer->bytes_read = 0;
	length_buffer->write_ptr = 0;

	//memset(length_buffer->buf_ptr, 0, LEN_BUF_SIZE + 1);
	
	return length_buffer;
}


/* the entry of buffer */
int
length_buf(LENGTH_BUF *buf_handler, char *data, size_t len)
{
	int64_t free_bytes;
	size_t ahead_free_bytes;
	size_t real_free_bytes;
	
	/* check whether the data is available */
	if (buf_handler == NULL || data == NULL || len <= 0)
	{
		if (buf_handler->buf_length > 0)return CONTINUE_BUF;
		return NO_DATA;
	}
	else
	{	
		real_free_bytes = LEN_BUF_SIZE - buf_handler->write_ptr;
		
		/* if the free buffer is smaller than 'len' */
		if (real_free_bytes < len)
		{
			buf_handler->last_data_len = buf_handler->write_ptr;
			buf_handler->write_ptr = 0;
			
			return FULL;
		}
		else
		{
			/* compute how many bytes i can write */
			ahead_free_bytes = (int64_t)(buf_handler->buf_length - buf_handler->bytes_read);
			
			memmove(buf_handler->buf_ptr + buf_handler->write_ptr, data, len);
			buf_handler->bytes_read += len;
			buf_handler->write_ptr += len;
			
			/* size of data received is larger than i can accept */ 
			if (len > ahead_free_bytes)
			{
				buf_handler->last_data_len = buf_handler->write_ptr - (len - ahead_free_bytes);
			}
			else
			{
				buf_handler->last_data_len = buf_handler->write_ptr;
			}

			free_bytes = (int64_t)(buf_handler->buf_length - buf_handler->bytes_read);
			
			if (free_bytes <= 0)
			{
				return STOP;
			}

			return CONTINUE_BUF;
		}
	}
	
	return CONTINUE_BUF;
}


/* function: set the stop length of buffer. 
 * stop length means that when the total data
 * length reach the 'stop length', the buffer STOP.
 * the stop length has no limitation. it can larger than the real length of
 * fixed length buffer(LEN_BUF_SIZE), of which will be called more than one
 * times and return FULL to inform the programmer to process the condition.
 * when the read bytes reach the 'stop length', STOP will return to inform a
 * completation of a buffer process. */
void
length_buf_set_sl(LENGTH_BUF *buf_handler, size_t len)
{
	buf_handler->buf_length = len;
}


/* reset buffer for next time use */
void
length_buf_reset(LENGTH_BUF *buf_handler)
{
	buf_handler->buf_length = 0;
	buf_handler->bytes_read = 0;
	buf_handler->write_ptr = 0;
	buf_handler->last_data_len = 0;
	
	//memset(buf_handler->buf_ptr, 0, LEN_BUF_SIZE + 1);
}


/* free the buffer and will no longer use */
int 
length_buf_clean(LENGTH_BUF *buf_handler)
{
    if (buf_handler == NULL)
	{
        return -1;
	}
	
	BUF_FREE(buf_handler);
	
	return 0;
}

/* ======== end of fix length buffer part ========= */


#ifndef __DECONST
#define __DECONST(type, var)    ((type)(uintptr_t)(const void *)(var))
#endif

/*
* Find the first occurrence of find in s, where the search is limited to the
* first slen characters of s.
*/
static char *
bsd_strnstr(const char *s, const char *find, size_t slen)
{
	char c, sc;
	size_t len;

	if ((c = *find++) != '\0') {
		len = strlen(find);
		do {
			do {
				if (slen < 1 || (sc = *s) == '\0')
					return (NULL);
				--slen;
				++s;
			} while (sc != c);
			if (len > slen)
				return (NULL);
		} while (strncmp(s, find, len) != 0);
		s--;
	}
	return (__DECONST(char *, s));
}




